/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package estg.dae.projecto.broker.controllers;

import estg.dae.projecto.bank.OperationType;
import estg.dae.projecto.bank.controllers.IBankControllerRemote;
import estg.dae.projecto.bank.impl.ElectronicOperationTO;
import estg.dae.projecto.bank.impl.NibValidator;
import estg.dae.projecto.broker.utils.BankServiceLocatorLocal;
import estg.dae.projecto.exceptions.OfflinetransferFailedException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Resource;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.EJB;
import javax.ejb.MessageDriven;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.ObjectMessage;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.NotSupportedException;
import javax.transaction.RollbackException;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;

/**
 *
 * @author mickael
 */
@MessageDriven(mappedName = "jms/Broker", activationConfig =  {
        @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge"),
        @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue")
    })
@TransactionManagement(value = TransactionManagementType.BEAN)
public class BrokerMessageBean implements MessageListener {
    @Resource
    private UserTransaction userTransaction;
    @EJB
    private BankServiceLocatorLocal serviceLocator;

    public BrokerMessageBean() {
    }

    public void onMessage(Message message) {
        IBankControllerRemote destinBank = null;
        IBankControllerRemote originBank = null;
        
        ObjectMessage objMessage = (ObjectMessage) message;
        ElectronicOperationTO eoTO = null;
        try {
            eoTO = (ElectronicOperationTO) objMessage.getObject();
            userTransaction.begin();

            //Valida ambos os nibs
            if (!NibValidator.validateNib(eoTO.getOriginNib()))
                throw new OfflinetransferFailedException("O NIB da conta de origem é inválido!");
            if (!NibValidator.validateNib(eoTO.getDestinNib()))
                throw new OfflinetransferFailedException("O NIB da conta de destino é inválido!");

            try {
                //carrega os serviços remotos de ambos os bancos
                originBank = serviceLocator.getBankService(eoTO.getOriginNib().substring(0, 4));
                destinBank = serviceLocator.getBankService(eoTO.getDestinNib().substring(0, 4));
            } catch (Exception ex) {
                Logger.getLogger(BrokerMessageBean.class.getName()).log(Level.SEVERE, null, ex);
                throw new OfflinetransferFailedException("Não foi possivel efectuar a ligação ao banco!");
            }

            if (eoTO.getOperationType() == OperationType.Transfer) {
                //Se for uma transferência
                //envia o valor para o banco destino
                destinBank.executeOnlineOperation(eoTO);
                //e confirma a execução junto do banco de origem
                originBank.confirmOnlineOperation(eoTO);
            } else if (eoTO.getOperationType() == OperationType.CheckDeposit) {
                //Se for um depósito em cheque
                //envia retira o valor do banco origem
                originBank.executeOnlineOperation(eoTO);
                //e envia a confirmação para o banco destino para disponibilizar o valor
                destinBank.confirmOnlineOperation(eoTO);
            }

            userTransaction.commit();

        } catch (RollbackException ex) {
            Logger.getLogger(BrokerMessageBean.class.getName()).log(Level.SEVERE, null, ex);
        } catch (HeuristicMixedException ex) {
            Logger.getLogger(BrokerMessageBean.class.getName()).log(Level.SEVERE, null, ex);
        } catch (HeuristicRollbackException ex) {
            Logger.getLogger(BrokerMessageBean.class.getName()).log(Level.SEVERE, null, ex);
        } catch (SecurityException ex) {
            Logger.getLogger(BrokerMessageBean.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IllegalStateException ex) {
            Logger.getLogger(BrokerMessageBean.class.getName()).log(Level.SEVERE, null, ex);
        } catch (NotSupportedException ex) {
            Logger.getLogger(BrokerMessageBean.class.getName()).log(Level.SEVERE, null, ex);
        } catch (SystemException ex) {
            Logger.getLogger(BrokerMessageBean.class.getName()).log(Level.SEVERE, null, ex);
        } catch (OfflinetransferFailedException e) {
            try {
                IBankControllerRemote requester;
                if (eoTO == null) {
                    return;
                }
                if (eoTO.getOperationType() == OperationType.Transfer) {
                    if (originBank != null) {
                        requester = originBank;
                    } else {
                        requester = serviceLocator.getBankService(eoTO.getOriginNib());
                    }
                } else if (eoTO.getOperationType() == OperationType.CheckDeposit) {
                    if (destinBank != null) {
                        requester = originBank;
                    } else {
                        requester = serviceLocator.getBankService(eoTO.getDestinNib());
                    }
                } else {
                    return;
                }
                userTransaction.rollback();

                //envia ao banco que efectuou o pedido a indicação que a transferência falhou
                userTransaction.begin();
                requester.failedOnlineOperation(eoTO, e.getMessage());
                userTransaction.commit();
            } catch (Exception ex) {
                Logger.getLogger(BrokerMessageBean.class.getName()).log(Level.SEVERE, null, ex);
            }

        }catch(JMSException e) {
            System.out.println("Erro JMS: " + e.getMessage());
        }
//        catch (Exception ex) {
//            Logger.getLogger(BrokerMessageBean.class.getName()).log(Level.SEVERE, null, ex);
//        }

    }
    
}
